<?php
/**
 * Database
 *
 * 数据库操作基类
 *
 * @author   songhuan <iphper@yeah.net>
 * @version   $Id: Database.php 1 2010-08-10 14:40:00Z $
 */
class Database extends Driver
{	
	/**
     * @var resource|object The driver level statement object/resource
     */
	protected $_stmt       = null;

	/**
     * Specifies the fetchMode of column names retrieved in queries
     * Options
     * PDO::FETCH_ASSOC (default)
     * PDO::FETCH_BOTH
     * PDO::FETCH_LAZY
     * PDO::FETCH_OBJ
	 * [PDO::FETCH_COLUMN|PDO::FETCH_GROUP]
     * @var integer
     */
    protected $_fetchMode  = PDO::FETCH_ASSOC;

    /**
     * @var string 最后一次执行的SQL
     */
	protected $_sql = '';
	
	/**
     * @var array 最后一次执行的Param
     */
	protected $_params = array();

	/**
     * 返回所有的结果集
     *
	 * @param string $sql.
	 * @param array|string|int $params.
	 * @param int $fetchMode PDO::FETCH_ASSOC(default) | PDO::FETCH_BOTH | PDO::FETCH_COLUMN ...
     * @return mixed Array, object, or scalar depending on fetch mode.
     */
 	public function fetchAll($sql, $params = array(), $fetchMode = null)
    {
        $this->query($sql, $params);
        return $this->fetch_all($fetchMode);
    }
    
    /**
     * 返回所有的结果集
     *
	 * @param string $sql.
	 * @param array|string|int $params.
     * @return mixed Array, object, or scalar depending on fetch mode.
     */
    public function fetchAssoc($sql, $params = array())
    {
        $this->query($sql, $params);
        $data = array();
        while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) 
        {
            $tmp = array_values(array_slice($row, 0, 1));
            $data[$tmp[0]] = $row;
        }
        return $data;
    }

	/**
     * 返回一条结果集
     *
	 * @param string $sql.
	 * @param array|string|int $params.
	 * @param int $fetchMode PDO::FETCH_ASSOC(default) | PDO::FETCH_BOTH | PDO::FETCH_COLUMN ...
     * @param int $cursor OPTIONAL Absolute, relative, or other.
     * @param int $offset OPTIONAL Number for absolute or relative cursors.
     * @return mixed array, object, or scalar depending on fetch mode.
     */
    public function fetchOne($sql, $params = array(), $fetchMode = null, $cursor = null, $offset = null)
    {
        $this->query($sql, $params);
        return $this->fetch_one($fetchMode, $cursor, $offset);
    }

    /**
     * 返回全部结果集中指定列的结果
     *
     * @param string $sql.
     * @param array|string|int $params.
     * @param int $column_number 列号
     * @return array.
     */
    public function fetchColumn($sql, $params = array(), $column_number = 0)
    {
        $this->query($sql, $params);
        return $this->fetch_column($column_number);
    }
    
    /**
     * 返回一个结果集中指定列的结果
     *
     * @param string $sql.
     * @param array|string|int $params.
     * @param int $column_number 列号
     * @return string|int.
     */
    public function fetchColumnOne($sql, $params = array(), $column_number = 0)
    {
        $this->query($sql, $params);
        return $this->fetch_column_one($column_number);
    }
	
	/**
     * 返回所有的结果集
     *
	 * @param int $fetchMode PDO::FETCH_ASSOC(default) | PDO::FETCH_BOTH | PDO::FETCH_COLUMN ...
     * @return mixed Array, object, or scalar depending on fetch mode.
     */
 	public function fetch_all($fetchMode = null)
    {
    	if (null === $fetchMode) 
    		return $this->_stmt->fetchAll($this->_fetchMode);
    	
        return $this->_stmt->fetchAll($fetchMode);
    }

	/**
     * 返回一条结果集
     *
     * @param int $fetchMode PDO::FETCH_ASSOC(default) | PDO::FETCH_BOTH | PDO::FETCH_COLUMN ...
     * @param int $cursor OPTIONAL Absolute, relative, or other.
     * @param int $offset OPTIONAL Number for absolute or relative cursors.
     * @return mixed array, object, or scalar depending on fetch mode.
     */
    public function fetch_one($fetchMode = null, $cursor = null, $offset = null)
    {
    	if (null === $fetchMode) 
    		return $this->_stmt->fetch($this->_fetchMode, $cursor, $offset);
    
    	return $this->_stmt->fetch($fetchMode, $cursor, $offset);
    }

    /**
     * 返回全部结果集中指定列的结果
     *
     * @param int $column_number 列号
     * @return array.
     */
    public function fetch_column($column_number = 0)
    {
        return $this->_stmt->fetchAll(PDO::FETCH_COLUMN, $column_number);
    }
	
    /**
     * 返回一个结果集中指定列的结果
     *
     * @param int $column_number 列号
     * @return string|int.
     */
    public function fetch_column_one($column_number = 0)
    {
        return $this->_stmt->fetchColumn($column_number);
    }

    /**
     * 执行数据库操作
     *
	 * @param string $sql.
	 * @param array $params.
     * @return bool 执行结果
     */
    public function query($sql, $params = array())
    {
		$this->_sql = &$sql;
		$this->_params = &$params;
		$this->getConnection();
		$this->_stmt = $this->_connection->prepare($sql);
		$this->bindParams($params);
		return $this->_execute();
    }

	/**
     * 向预处理绑定一个数组
     * 
     * @return void
     */
    public function bindParams($params = null)
    {
		if (is_array($params)) 
		{
			$parameter = 1;
			foreach ($params as $variable)
				$this->bindParam($parameter++, $variable);
		}
		else 
			$this->bindParam(1, $params);
    }

	/**
     * 向预处理绑定一个参数
     *
     * @param mixed $parameter Name the parameter, either integer or string.
     * @param mixed $variable  Reference to PHP variable containing the value.
     * @param mixed $type      OPTIONAL Datatype of SQL parameter.
     * @param mixed $length    OPTIONAL Length of SQL parameter.
     * @param mixed $options   OPTIONAL Other options.
     * @return bool
     */
    public function bindParam($parameter, $variable, $type = null, $length = null, $options = null)
    {
        try 
        {
            if (null === $type) 
            {
                if (is_bool($variable)) 
                    $type = PDO::PARAM_BOOL;
                else if (null === $variable) 
                    $type = PDO::PARAM_NULL;
                else if (is_integer($variable))
                    $type = PDO::PARAM_INT;
                else
                    $type = PDO::PARAM_STR;
            }
            return $this->_stmt->bindParam($parameter, $variable, $type, $length, $options);
        } 
		catch (PDOException $e) 
		{
            throw new Exception($e->getMessage());
        }
    }

	/**
     * 执行预处理
     *
     * @param array $params OPTIONAL Values to bind to parameter placeholders.
     * @return bool
     */
    protected function _execute(array $params = null)
    {
        try 
        {
            if (null !== $params) 
                return $this->_stmt->execute($params);
            else 
                return $this->_stmt->execute();
        }
		catch (PDOException $e) 
		{
            throw new Exception($e->getMessage() . '[SQL:' . $this->_sql . ']');
        }
    }

	/**
     * 为statement设置默认的fetch mode
     *
     * @param int $mode The fetch mode.
     */
    public function setFetchMode($fetchMode = PDO::FETCH_ASSOC)
	{
		switch ($fetchMode) 
		{
			case PDO::FETCH_NUM:
			case PDO::FETCH_ASSOC:
			case PDO::FETCH_BOTH:
			case PDO::FETCH_OBJ:
				$this->_fetchMode = $fetchMode;
				break;
			case PDO::FETCH_BOUND:
			default:
				throw new Exception('invalid fetch mode');
				break;
		}
	}
    
	/**
     * 返回受影响的行的条数
     *
     * @return int
     */
	public function rowCount()
    {
    	try 
    	{
            return $this->_stmt->rowCount();
        } 
		catch (PDOException $e) 
		{
            throw new Exception($e->getMessage());
        } 
    }
    
	public function columnCount()
    {
        try 
        {
            return $this->_stmt->columnCount();
        } 
		catch (PDOException $e) 
		{
            throw new Exception($e->getMessage());
        }
    }

    public function errorCode()
    {
        try 
        {
            return $this->_stmt->errorCode();
        } 
		catch (PDOException $e) 
		{
            throw new Exception($e->getMessage());
        }
    }

    public function errorInfo()
    {
        try 
        {
           return $this->_stmt->errorInfo();
        } 
		catch (PDOException $e) 
		{
           throw new Exception($e->getMessage());
        }
    }
    
    /**
	 * @return 最后一次执行的SQL语句 - 测试用
	 */
	public function getSql()
	{
		return $this->_sql;
	}
	
    /**
	 * @return 最后一次执行的Param参数 - 测试用
	 */
	public function getParams()
	{
		return $this->_params;
	}
}
